package com.river.springbootdemo.thinkingjava.generics;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

interface Combiner<T> {
    T combine(T x, T y);
}

interface UnaryFunction<R, T> {
    R function(T x);
}

//一元函数
interface Collector<T> extends UnaryFunction<T, T> {
    T result();//Extract result of collecting parameter;提取采集参数的结果
}

//一元谓语
interface UnaryPredicate<T> {
    boolean test(T x);
}

public class Functional {
    //Caller the Combiner object on each element to combine  调用每个元素上的组合器对象进行组合
    //it with a running result,which is finally returned: 它带有一个运行结果，最终返回:
    public static <T> T reduce(Iterable<T> seq, Combiner<T> combiner) {
        Iterator<T> iterator = seq.iterator();
        if (iterator.hasNext()) {
            T result = iterator.next();
            while (iterator.hasNext()) {
                combiner.combine(result, iterator.next());
            }
            return result;
        }
        //if seq is the empty list:
        return null;//or throw exception
    }

    //take a function object and call it on each object in
    //the list,ignoring the return value,the function
    //object may act as a collecting parameter ,so it is
    //returned at the end 取一个函数对象，并对列表中的每个对象调用它，忽略返回值，函数对象可以作为一个收集参数，因此在最后返回它
    public static <T> Collector<T> foreach(Iterable<T> seq, Collector<T> func) {
        for (T t : seq) {
            func.function(t);
        }
        return func;
    }

    //creates a list of results by calling a
    //function object for each object in the list 通过为列表中的每个对象调用函数对象来创建结果列表
    public static <R, T> List<R> transform(Iterable<T> seq, UnaryFunction<R, T> func) {
        List<R> result = new ArrayList<>();
        for (T t : seq) {
            result.add(func.function(t));
        }
        return result;
    }

    //applies a unary predicate to each item in a sequence
    //and returns a list of items that produced "true"
    public static <T> List<T> filter(Iterable<T> seq, UnaryPredicate<T> pred) {
        List<T> result = new ArrayList<>();
        for (T t : seq) {
            if (pred.test(t)) {
                result.add(t);
            }
        }
        return result;
    }

    //to use the above generic methods ,we need to create
    //function objects to adapt to our particular needs
    static class IntegerAdder implements Combiner<Integer> {

        @Override
        public Integer combine(Integer x, Integer y) {
            return x + y;
        }
    }

    static class IntegerSubtracter implements Combiner<Integer> {

        @Override
        public Integer combine(Integer x, Integer y) {
            return x - y;
        }
    }

    static class DecimalAdder implements Combiner<BigDecimal> {

        @Override
        public BigDecimal combine(BigDecimal x, BigDecimal y) {
            return x.add(y);
        }
    }

    static class BigIntegerAdder implements Combiner<BigInteger> {

        @Override
        public BigInteger combine(BigInteger x, BigInteger y) {
            return x.add(y);
        }
    }

    static class AtomicLongAdder implements Combiner<AtomicLong> {

        @Override
        public AtomicLong combine(AtomicLong x, AtomicLong y) {
            return new AtomicLong(x.addAndGet(y.get()));
        }
    }

    //we can even make a UnaryFunction with an "ulp"
    static class BigDecimalUlp implements UnaryFunction<BigDecimal, BigDecimal> {

        @Override
        public BigDecimal function(BigDecimal x) {
            return x.ulp();
        }
    }

    static class GreaterThan<T extends Comparable<T>> implements UnaryPredicate<T> {
        private T bound;

        public GreaterThan(T bound) {
            this.bound = bound;
        }

        @Override
        public boolean test(T x) {
            return x.compareTo(bound) > 0;
        }
    }

    static class MutiplyingIntegerCollector implements Collector<Integer> {
        private Integer val = 1;

        @Override
        public Integer function(Integer x) {
            val *= x;
            return val;
        }

        @Override
        public Integer result() {
            return val;
        }
    }

    public static void main(String[] args) {
        //Generics ,varargs & boxing working together:
        List<Integer> li= Arrays.asList(1,2,3,4,5,6,7);
        Integer result=reduce(li,new IntegerAdder());
        System.out.println(result);
    }
}
